package model

import (
	"errors"
	"fmt"
	"github.com/xormplus/xorm"
	"go-vue/admin/conf"
	"go-vue/admin/utils"
	"log"
)

// SystemMenu 系统菜单
type SystemMenu struct {
	Id               int64      `xorm:"autoincr id"`
	Pid              int64      `xorm:"pid"`
	Level            int        `xorm:"level"`
	Sort             int        `xorm:"sort"`
	Alias            string     `xorm:"alias"`
	Path             string     `xorm:"path"`
	Component        string     `xorm:"component"`
	Icon             string     `xorm:"icon"`
	Title            string     `xorm:"title"`
	Type             string     `xorm:"type"`
	Affix            string     `xorm:"affix"`
	Hidden           int        `xorm:"hidden"`
	HiddenBreadcrumb int        `xorm:"hidden_breadcrumb"`
	Active           string     `xorm:"active"`
	Redirect         string     `xorm:"redirect"`
	Color            string     `xorm:"color"`
	CreateTime       string     `xorm:"create_time"`
	UpdateTime       string     `xorm:"update_time"`
	State            conf.State `xorm:"state"`
}

// SystemMenuList 菜单列表
type SystemMenuList struct {
	Alias            string            `json:"name"`
	Path             string            `json:"path"`
	Sort             int               `json:"sort"`
	Component        string            `json:"component,omitempty"`
	Meta             Meta              `json:"meta"`
	Icon             string            `json:"icon,omitempty"`
	Title            string            `json:"title,omitempty"`
	Color            string            `json:"color,omitempty"`
	Type             string            `json:"type,omitempty"`
	Hidden           int               `json:"hidden,omitempty"`
	HiddenBreadcrumb int               `json:"hiddenBreadcrumb,omitempty"`
	Affix            string            `json:"affix,omitempty"`
	Children         []*SystemMenuList `json:"children,omitempty"`
}

// MenuApiList 接口权限
type MenuApiList struct {
	Code   string `json:"code"`
	Remark string `json:"remark"`
}

// MenuSortInput 拖动排序输入
type MenuSortInput struct {
	ParentId   int64  `json:"parentId"`
	DraggingId int64  `json:"draggingId"`
	DropId     int64  `json:"dropId"`
	DropType   string `json:"dropType"`
}

// DeleteInputIds 删除菜单
type DeleteInputIds struct {
	Ids []int64 `json:"ids"`
}

func (menu *SystemMenu) One() *SystemMenu {
	exist, err := conf.DbEngine.Get(menu)
	if err != nil || exist == false {
		return nil
	}
	return menu
}

// GetAllMenu 获取所有菜单，编辑用
func (menu *SystemMenu) GetAllMenu() []*SystemMenuList {
	var menuList []SystemMenu
	err := conf.DbEngine.OrderBy("level asc,sort asc").Find(&menuList)
	if err != nil {
		log.Println(err)
		return nil
	}
	return menu.BuildMenuChild(0, menuList)
}

// GetMyMenu 获取我的所有菜单
func (menu *SystemMenu) GetMyMenu(ids []int64) []*SystemMenuList {
	var menuList []SystemMenu
	err := conf.DbEngine.In("id", ids).Where("state = ?", conf.StateOn).OrderBy("level asc,sort asc").Find(&menuList)
	if err != nil {
		log.Println(err)
		return nil
	}
	return menu.BuildMenuChild(0, menuList)
}

// BuildMenuChild 递归获取子菜单
func (menu *SystemMenu) BuildMenuChild(pid int64, menuList []SystemMenu) []*SystemMenuList {
	var treeList []*SystemMenuList
	for _, v := range menuList {
		if pid == v.Pid {
			node := &SystemMenuList{
				Alias:     v.Alias,
				Path:      v.Path,
				Component: v.Component,
				Sort:      v.Sort,
				Meta: Meta{
					Icon:             v.Icon,
					Title:            v.Title,
					Type:             v.Type,
					Hidden:           v.Hidden,
					HiddenBreadcrumb: v.HiddenBreadcrumb,
					Affix:            v.Affix,
				},
				Icon:             v.Icon,
				Title:            v.Title,
				Type:             v.Type,
				Hidden:           v.Hidden,
				HiddenBreadcrumb: v.HiddenBreadcrumb,
				Affix:            v.Affix,
				Children:         nil,
			}
			child := v.BuildMenuChild(v.Id, menuList)
			if len(child) != 0 {
				node.Children = child
			}
			treeList = append(treeList, node)
		}
	}
	return treeList
}

// Meta
type Meta struct {
	Icon             string `json:"icon,omitempty"`
	Title            string `json:"title,omitempty"`
	Color            string `json:"color,omitempty"`
	Type             string `json:"type,omitempty"`
	Hidden           int    `json:"hidden,omitempty"`
	HiddenBreadcrumb int    `json:"hiddenBreadcrumb,omitempty"`
	Affix            string `json:"affix,omitempty"`
}

// SystemMenuEditList 菜单列表(菜单管理编辑用)
type SystemMenuEditList struct {
	Id               int64                 `json:"id"`
	ParentId         int64                 `json:"parentId"`
	Alias            string                `json:"name"`
	Path             string                `json:"path"`
	Redirect         string                `json:"redirect"`
	Active           string                `json:"active"`
	Sort             int                   `json:"sort"`
	State            conf.State            `json:"state"`
	Component        string                `json:"component,omitempty"`
	Meta             Meta                  `json:"meta,omitempty""`
	Icon             string                `json:"icon,omitempty"`
	Title            string                `json:"title,omitempty"`
	Color            string                `json:"color,omitempty"`
	Type             string                `json:"type,omitempty"`
	Hidden           int                   `json:"hidden,omitempty"`
	HiddenBreadcrumb int                   `json:"hiddenBreadcrumb,omitempty"`
	Affix            string                `json:"affix,omitempty"`
	Children         []*SystemMenuEditList `json:"children,omitempty"`
	ApiList          []SystemMenuApiList   `json:"apiList,omitempty"`
}

// GetAllEditMenu 获取系统所有菜单(菜单管理编辑用)
func (menu *SystemMenu) GetAllEditMenu() []*SystemMenuEditList {
	var menuList []SystemMenu
	err := conf.DbEngine.OrderBy("level asc,sort asc").Find(&menuList)
	if err != nil {
		log.Println(err)
		return nil
	}

	var listEdit []*SystemMenuEditList
	if len(menuList) > 0 {
		apiList := new(SystemMenuApiList).GetAllMenuApiList()
		listEdit = menu.BuildMenuChildEdit(0, menuList, apiList)
	}
	return listEdit
}

// BuildMenuChildEdit 递归获取子菜单(菜单管理编辑用)
func (menu *SystemMenu) BuildMenuChildEdit(pid int64, menuList []SystemMenu, apiList map[int64][]SystemMenuApiList) []*SystemMenuEditList {
	var treeList []*SystemMenuEditList
	for _, v := range menuList {
		if pid == v.Pid {
			apis := make([]SystemMenuApiList, 0)
			if lst, ok := apiList[v.Id]; ok == true {
				apis = append(apis, lst...)
			}
			node := &SystemMenuEditList{
				Id:        v.Id,
				ParentId:  v.Pid,
				Sort:      v.Sort,
				Alias:     v.Alias,
				Path:      v.Path,
				Component: v.Component,
				Active:    v.Active,
				Redirect:  v.Redirect,
				State:     v.State,
				Meta: Meta{
					Icon:             v.Icon,
					Title:            v.Title,
					Type:             v.Type,
					Affix:            v.Affix,
					Hidden:           v.Hidden,
					Color:            v.Color,
					HiddenBreadcrumb: v.HiddenBreadcrumb,
				},
				Icon:             v.Icon,
				Title:            v.Title,
				Type:             v.Type,
				Affix:            v.Affix,
				Hidden:           v.Hidden,
				Color:            v.Color,
				HiddenBreadcrumb: v.HiddenBreadcrumb,
				ApiList:          apis,
			}
			child := v.BuildMenuChildEdit(v.Id, menuList, apiList)
			if len(child) != 0 {
				node.Children = child
			}
			treeList = append(treeList, node)
		}
	}
	return treeList
}

// SystemMenuPermission 菜单列表(菜单管理编辑用)
type SystemMenuPermission struct {
	ParentAlias string                  `json:"parentName"`
	Alias       string                  `json:"name"`
	Component   string                  `json:"component,omitempty"`
	Icon        string                  `json:"icon,omitempty"`
	Title       string                  `json:"title,omitempty"`
	Children    []*SystemMenuPermission `json:"children,omitempty"`
}

// GetAllMenuPermission 获取系统菜单权限
func (menu *SystemMenu) GetAllMenuPermission() []*SystemMenuPermission {
	var menuList []SystemMenu
	err := conf.DbEngine.Where("state = ?", conf.StateOn).OrderBy("level asc,sort asc").Find(&menuList)
	if err != nil {
		log.Println(err)
		return nil
	}

	var listEdit []*SystemMenuPermission
	if len(menuList) > 0 {
		apiList := new(SystemMenuApiList).GetAllMenuApiList()
		listEdit = menu.BuildMenuChildPermission(0, menuList, apiList)
	}
	return listEdit
}

// BuildMenuChildPermission 递归获取子菜单(菜单管理编辑用)
func (menu *SystemMenu) BuildMenuChildPermission(pid int64, menuList []SystemMenu, apiList map[int64][]SystemMenuApiList) []*SystemMenuPermission {
	var treeList []*SystemMenuPermission
	for _, v := range menuList {
		if pid == v.Pid {
			node := &SystemMenuPermission{
				Alias:     v.Alias,
				Component: v.Component,
				Icon:      v.Icon,
				Title:     v.Title,
			}
			child := v.BuildMenuChildPermission(v.Id, menuList, apiList)
			if len(child) != 0 {
				node.Children = child
			}
			apis := make([]*SystemMenuPermission, 0)
			if lst, ok := apiList[v.Id]; ok == true {
				for _, item := range lst {
					tmp := &SystemMenuPermission{
						ParentAlias: v.Alias,
						Alias:       item.Code,
						Title:       item.Remark,
					}
					apis = append(apis, tmp)
				}
			}
			node.Children = append(node.Children, apis...)

			treeList = append(treeList, node)
		}
	}
	return treeList
}

// SaveEditMenu 编辑系统菜单(菜单管理编辑用)
func (menu *SystemMenu) SaveEditMenu(input *SystemMenuEditList) (id int64, err error) {
	//主表信息
	menuInfo := &SystemMenu{
		Pid:              input.ParentId,
		Alias:            input.Alias,
		Path:             input.Path,
		Component:        input.Component,
		Icon:             input.Icon,
		Title:            input.Title,
		Type:             input.Type,
		Affix:            input.Affix,
		Hidden:           input.Hidden,
		HiddenBreadcrumb: input.HiddenBreadcrumb,
		Color:            input.Color,
		Active:           input.Active,
		Redirect:         input.Redirect,

		UpdateTime: utils.DateNowString(),
		State:      input.State,
	}

	var apiList []*SystemMenuApiList
	if len(input.ApiList) > 0 {
		for _, item := range input.ApiList {
			tmp := &SystemMenuApiList{
				MenuId: input.Id,
				Code:   item.Code,
				Remark: item.Remark,
			}
			apiList = append(apiList, tmp)
		}
	}

	//开启事务
	session := conf.DbEngine.NewSession()
	defer session.Close()

	err = session.Begin()
	if err != nil {
		return 0, err
	}

	//更新
	if input.Id != 0 {
		//1、更新主表
		_, err = session.Where("id = ?", input.Id).Cols("pid,alias,path,component,icon,title,type,affix,hidden,hidden_breadcrumb,active,redirect,color,update_time,state").Update(menuInfo)
		if err != nil {
			_ = session.Rollback()
			return 0, err
		}

		//region 全删全插---开始
		//删除附表
		_, err = session.Delete(&SystemMenuApiList{MenuId: input.Id})
		if err != nil {
			_ = session.Rollback()
			return 0, err
		}
		//写入附表
		if len(apiList) > 0 {
			_, err = session.Insert(apiList)
			if err != nil {
				_ = session.Rollback()
				return 0, err
			}
		}
		//region 全删全插---结束
		id = input.Id
	} else { //新增的
		menuInfo.CreateTime = utils.DateNowString()
		menuInfo.State = conf.StateOff

		max := &SystemMenu{}
		//获取最大排序 直接更新
		_, err := conf.DbEngine.Where("pid=?", input.ParentId).OrderBy("sort desc").Cols("sort").Get(max)
		if err != nil {
			_ = session.Rollback()
			return 0, err
		}
		menuInfo.Sort = max.Sort + 1

		level := &SystemMenu{}
		//当前需要更新到哪个层级
		_, err = conf.DbEngine.Where("id=?", input.ParentId).Cols("level").Get(level)
		if err != nil {
			_ = session.Rollback()
			return 0, err
		}
		menuInfo.Level = level.Level + 1

		_, err = session.Insert(menuInfo)
		if err != nil {
			_ = session.Rollback()
			return 0, err
		}
		fmt.Printf("menuInfo:%+v\n", menuInfo)
		id = menuInfo.Id
	}

	err = session.Commit()
	if err != nil {
		_ = session.Rollback()
		return 0, err
	}
	return id, err
}

// SortEditMenu 系统菜单排序(菜单管理编辑用)
func (menu *SystemMenu) SortEditMenu(input *MenuSortInput) error {
	menu.Id = input.DropId
	res := menu.One()
	if res == nil {
		return errors.New("更改节点不存在")
	}

	dragging := &SystemMenu{
		Id: input.DraggingId,
	}
	res = dragging.One()
	if res == nil {
		return errors.New("拖拽的节点不存在")
	}

	//拖拽到新节点里面
	if input.DropType == "inner" {
		dragging.Pid = menu.Id

		max := &SystemMenu{}
		//获取最大排序 直接更新
		_, err := conf.DbEngine.Where("pid=?", menu.Id).OrderBy("sort desc").Cols("sort").Get(max)
		if err != nil {
			return err
		}
		dragging.Sort = max.Sort + 1

		//更新数据
		_, err = conf.DbEngine.Where("id = ?", dragging.Id).Cols("pid,sort").Update(dragging)
		if err != nil {
			return err
		}
		return nil
	}

	var (
		sql    string
		sqlOld string
	)

	//同一个级别排序
	if dragging.Pid == menu.Pid {
		if dragging.Sort < menu.Sort { //拖动节点顺序变大
			if input.DropType == "before" {
				sql = fmt.Sprintf("update system_menu set sort = sort-1 where pid = %d and sort > %d and sort < %d", menu.Pid, dragging.Sort, menu.Sort)
				dragging.Sort = menu.Sort - 1
			} else if input.DropType == "after" {
				sql = fmt.Sprintf("update system_menu set sort = sort-1 where pid = %d and sort > %d and sort <= %d", menu.Pid, dragging.Sort, menu.Sort)
				dragging.Sort = menu.Sort
			}
		} else { //拖动节点顺序变小
			if input.DropType == "before" {
				sql = fmt.Sprintf("update system_menu set sort = sort+1 where pid = %d and sort >= %d and sort < %d", menu.Pid, menu.Sort, dragging.Sort)
				dragging.Sort = menu.Sort
			} else if input.DropType == "after" {
				sql = fmt.Sprintf("update system_menu set sort = sort+1 where pid = %d and sort > %d and sort < %d", menu.Pid, menu.Sort, dragging.Sort)
				dragging.Sort = menu.Sort + 1
			}
		}
	} else { //从别的节点子项目拖到新的节点子项目
		//更新老节点的排序
		sqlOld = fmt.Sprintf("update system_menu set sort = sort-1 where pid = %d and sort > %d", dragging.Pid, dragging.Sort)

		dragging.Pid = menu.Pid
		dragging.Level = menu.Level
		if input.DropType == "before" {
			sql = fmt.Sprintf("update system_menu set sort = sort+1 where pid = %d and sort >= %d", menu.Pid, menu.Sort)
			dragging.Sort = menu.Sort
		} else if input.DropType == "after" {
			sql = fmt.Sprintf("update system_menu set sort = sort+1 where pid = %d and sort > %d", menu.Pid, menu.Sort)
			dragging.Sort = menu.Sort + 1
		}
	}

	session := conf.DbEngine.NewSession()
	defer session.Close()

	session.Begin()
	//更新老节点的排序
	if sqlOld != "" {
		_, err := session.Exec(sqlOld)
		if err != nil {
			session.Rollback()
			return err
		}
	}
	//先更新其他数据的顺序
	_, err := session.Exec(sql)
	if err != nil {
		session.Rollback()
		return err
	}
	//更新拖拽数据顺序
	_, err = session.Where("id = ?", dragging.Id).Cols("pid,sort,level").Update(dragging)
	if err != nil {
		session.Rollback()
		return err
	}
	err = session.Commit()
	if err != nil {
		session.Rollback()
		return err
	}
	return nil
}

// DelEditMenu 删除列表
func (menu *SystemMenu) DelEditMenu(input *DeleteInputIds) error {
	delList := menu.treeDel(input.Ids, input.Ids)
	fmt.Printf("delList === %+v\n", delList)
	if len(delList) == 0 {
		return errors.New("不存在删除的节点")
	}

	session := conf.DbEngine.NewSession()
	defer session.Close()

	session.Begin()
	//删除主表
	_, err := session.In("id", delList).Delete(menu)
	if err != nil {
		session.Rollback()
		return err
	}

	//删除副标 API
	_, err = session.In("id", delList).Delete(&SystemMenuApiList{})
	if err != nil {
		session.Rollback()
		return err
	}

	//全表重新排序
	menu.SortAll(0, session)
	err = session.Commit()
	if err != nil {
		session.Rollback()
		return err
	}
	return nil
}

//SortAll 全表重新排序(递归)
func (menu *SystemMenu) SortAll(pid int64, session *xorm.Session) {
	var menuList []SystemMenu
	session.Where("pid = ?", pid).OrderBy("level asc,sort asc").Find(&menuList)
	num := 1
	for _, item := range menuList {
		item.Sort = num
		_, err := session.Where("id = ?", item.Id).Cols("sort").Update(item)
		if err != nil {
			session.Rollback()
		}

		menu.SortAll(item.Id, session)
		num++
	}
}

//递归删除
func (menu *SystemMenu) treeDel(ids []int64, exist []int64) []int64 {
	list := make([]SystemMenu, 0)
	err := conf.DbEngine.In("pid", ids).Cols("id").Find(&list)
	if err != nil {
		return nil
	}
	if len(list) > 0 {
		newIds := make([]int64, 0)
		for _, item := range list {
			newIds = append(newIds, item.Id)
		}
		exist = append(exist, newIds...)
		return menu.treeDel(newIds, exist)
	}
	return exist
}

// GetAllMenuIds 获取系统所有菜单权限api
func (menu *SystemMenu) GetAllMenuIds(permissions []string) []int64 {
	var list []SystemMenu
	err := conf.DbEngine.In("alias", permissions).Distinct("id").Find(&list)
	if err != nil {
		log.Println(err)
		return nil
	}
	var ids []int64
	for _, apiList := range list {
		ids = append(ids, apiList.Id)
	}
	return ids
}
